﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Extended_Button
{
    public partial class ExtendedButton : Button
    {
        Bitmap bBackground;
        int iBorderWidth = 2;
        Color cBorder = Color.Black;
        ButtonStyle bStyle = ButtonStyle.Normal;
        int iCornerStrength = 60;

        public enum ButtonStyle
        {
            Normal,
            Round,
            Rectangle,
            Trapeze
        }

        [Description("Can be used to adjust the strength of the corners on the button."), Category("Appearance"), Browsable(true)]
        public int CornerStrength
        {
            get { return iCornerStrength; }
            set { 
                iCornerStrength = value;
                this.Refresh();
            }
        }

        [Description("Sets the border width of the button."), Category("Appearance"), Browsable(true)]
        public int BorderWidth
        {
            get { return iBorderWidth; }
            set 
            { 
                iBorderWidth = value;
                this.Refresh();
            }
        }

        [Description("Sets the border color of the button."), Category("Appearance"), Browsable(true)]
        public Color BorderColor
        {
            get { return cBorder; }
            set 
            { 
                cBorder = value; 
                this.Refresh(); 
            }
        }

        [Description("Sets the style of the button."), Category("Advanced Appearance"), Browsable(true)]
        public ButtonStyle Style
        {
            get { return bStyle; }
            set
            {
                bStyle = value;
                this.Refresh();
            }
        }

        public ExtendedButton()
        {
            InitializeComponent();
        }

        protected override void OnPaint(PaintEventArgs pe)
        {
            bBackground = new Bitmap(this.Width, this.Height);
            Graphics gGraphic = Graphics.FromImage(bBackground);
            RectangleF rTextRegion;
            StringFormat sTextFormat;

            switch (bStyle)
            {
                case ButtonStyle.Normal:
                    base.OnPaint(pe);
                    break;

                case ButtonStyle.Round:
                    Pen pBorder = new Pen(cBorder, iBorderWidth + 1);
                    pBorder.Alignment = System.Drawing.Drawing2D.PenAlignment.Inset;
                    
                    int iCurveWidth = (int)Math.Round((this.Width / 100f) * iCornerStrength, 0);
                    gGraphic.DrawArc(pBorder, new Rectangle(iBorderWidth / 2, 0, iCurveWidth, this.Height - 1), 90, 180);
                    gGraphic.DrawArc(pBorder, new Rectangle(this.Width - (iCurveWidth + 1 + (iBorderWidth / 2)), 0, iCurveWidth, this.Height - 1), 270, 180);

                    FillBetween(this.BackColor, cBorder, iBorderWidth);

                    rTextRegion = new RectangleF(iCurveWidth / 3, iBorderWidth + 2, this.Width - iCurveWidth / 3 * 2, this.Height - (iBorderWidth + 2) * 2);
                    sTextFormat = CalculateTextAlign();

                    gGraphic.DrawString(this.Text, this.Font, new SolidBrush(this.ForeColor), rTextRegion, sTextFormat);

                    pBorder.Dispose();
                    gGraphic.Dispose();
                    break;

                case ButtonStyle.Rectangle:
                    gGraphic.DrawRectangle(new Pen(cBorder, iBorderWidth), 0, 0, this.Width, this.Height);
                    gGraphic.FillRectangle(new SolidBrush(this.BackColor), iBorderWidth, iBorderWidth, this.Width - iBorderWidth * 2, this.Height - iBorderWidth * 2);

                    rTextRegion = new RectangleF(iBorderWidth + 2, iBorderWidth + 2, this.Width - (iBorderWidth + 2) * 2, this.Height - (iBorderWidth + 2) * 2);
                    sTextFormat = CalculateTextAlign();

                    gGraphic.DrawString(this.Text, this.Font, new SolidBrush(this.ForeColor), rTextRegion, sTextFormat);
                    break;
            }

            if (bStyle != ButtonStyle.Normal)
            {
                pe.Graphics.Clear(this.Parent.BackColor);
                pe.Graphics.DrawImage(bBackground, 0, 0);
            }
        }

        private StringFormat CalculateTextAlign()
        {
            StringFormat sTextAlign = new StringFormat();

            switch (this.TextAlign)
            {
                case ContentAlignment.TopLeft:
                    sTextAlign.Alignment = StringAlignment.Near;
                    sTextAlign.LineAlignment = StringAlignment.Near;
                    break;
                case ContentAlignment.TopCenter:
                    sTextAlign.Alignment = StringAlignment.Center;
                    sTextAlign.LineAlignment = StringAlignment.Near;
                    break;
                case ContentAlignment.TopRight:
                    sTextAlign.Alignment = StringAlignment.Far;
                    sTextAlign.LineAlignment = StringAlignment.Near;
                    break;
                case ContentAlignment.MiddleLeft:
                    sTextAlign.Alignment = StringAlignment.Near;
                    sTextAlign.LineAlignment = StringAlignment.Center;
                    break;
                case ContentAlignment.MiddleCenter:
                    sTextAlign.Alignment = StringAlignment.Center;
                    sTextAlign.LineAlignment = StringAlignment.Center;
                    break;
                case ContentAlignment.MiddleRight:
                    sTextAlign.Alignment = StringAlignment.Far;
                    sTextAlign.LineAlignment = StringAlignment.Center;
                    break;
                case ContentAlignment.BottomLeft:
                    sTextAlign.Alignment = StringAlignment.Near;
                    sTextAlign.LineAlignment = StringAlignment.Far;
                    break;
                case ContentAlignment.BottomCenter:
                    sTextAlign.Alignment = StringAlignment.Center;
                    sTextAlign.LineAlignment = StringAlignment.Far;
                    break;
                case ContentAlignment.BottomRight:
                    sTextAlign.Alignment = StringAlignment.Far;
                    sTextAlign.LineAlignment = StringAlignment.Far;
                    break;
            }//Case

            return sTextAlign;
        }

        #region Fill Between Methods
        private void FillBetween(Color cFill, Color cBetween)
        {
            this.FillBetween(cFill.ToArgb(), cBetween.ToArgb(), 0);
        }

        private void FillBetween(int iFillArgb, int iBetweenArgb)
        {
            this.FillBetween(iFillArgb, iBetweenArgb, 0);
        }

        private void FillBetween(Color cFill, Color cBetween, int iBorderWidth)
        {
            this.FillBetween(cFill.ToArgb(), cBetween.ToArgb(), iBorderWidth);
        }

        private void FillBetween(int iFillArgb, int iBetweenArgb, int iBorderWidth)
        {
            bool StartFill = false;
            bool HasDrawn;
            Color cFill = Color.FromArgb(iFillArgb);
            Color cBetween = Color.FromArgb(iBetweenArgb);
            for (int y = 0; y < bBackground.Height; y++)
            {
                HasDrawn = true;
                for (int x = 0; x < bBackground.Width; x++)
                {
                    Color cBackgroundPixel = bBackground.GetPixel(x, y);

                    if (cBackgroundPixel == cBetween && HasDrawn)
                    {
                        HasDrawn = false;
                        StartFill = ToggleBoolValue(StartFill);
                    }
                    else if (StartFill && cBackgroundPixel != cBetween)
                    {
                        HasDrawn = true;

                        if (y <= iBorderWidth || y >= this.Height - (iBorderWidth + 1))
                            bBackground.SetPixel(x, y, cBetween);
                        else
                            bBackground.SetPixel(x, y, cFill);
                    }// if
                }// x loop
            }// y loop
        }
        #endregion

        /// <summary>
        /// Toggles boolean's between there two values (true and false).
        /// </summary>
        /// <param name="bValue">Bool value to be toggled.</param>
        /// <returns>Opposite value to that received.</returns>
        private bool ToggleBoolValue(bool bValue)
        {
            if (bValue)
                bValue = false;
            else
                bValue = true;

            return bValue;
        }
    }
}
